# -*- coding:utf-8 -*-
# author: lkz
# date: 2016/11/23 15:42

import os
import sys
import yaml
import json
import time
import redis
import signal
import socket
import argparse
import traceback

from log import logger
from ping import Ping
from tsdb import send_to_tsdb


def parse_args():
    parser = argparse.ArgumentParser(description="fast ping", version="1.0")

    parser.add_argument("-c", "--config", dest="config", help="configuration file path")
    parser.add_argument("-d", "--debug", dest="debug",  action="store_true", help="enable debug mode")

    args = parser.parse_args()

    return args


class Master(object):
    stop_signals = (signal.SIGINT, signal.SIGTERM, signal.SIGQUIT)

    def __init__(self, config_file=None, debug=True):
        self.debug = debug

        if not config_file:
            _cur_dir = os.path.dirname(os.path.abspath(__file__))
            config_file = os.path.join(_cur_dir, "config.yaml")
        else:
            config_file = config_file

        if not os.path.isfile(config_file):
            logger.error("config file %s is not exist" % config_file)
            sys.exit(1)

        with open(config_file, mode="r") as f:
            config = yaml.load(f)

        self.task_name = config.get("task_name")
        self.task_id = config.get("task_id")

        self.__local_ip = config.get("local_ip")

        args = config.get("args", {})
        assert bool(args), u"配置文件缺少args字段"

        targets = args.get("targets")
        assert targets, u"配置文件args字段缺少targets参数"

        period = args.get("period", 20)
        assert period >= 20, u"ping间隔不能小于20秒, 请修改参数args/period"

        packets_once = args.get("packets_once", 10)
        assert 1 <= packets_once <= 20, u"每次发送的icmp包个数不能超过20个, 不能少于1个. 请修改参数args/packets_once"

        self.__ping = Ping(targets, period, packets_once, data_process_func=self.__send_data, run_forever=True)

        self.__data_server_ip = config.get("data_server")["ip"]
        self.__data_server_port = config.get("data_server")["port"]
        self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        redis_server = config.get("redis_server")
        self.__redis = redis.Redis(**redis_server)
        self.__redis_key = "task_result:%s" % self.task_id
        self.__alive_key = "alive:%s" % self.task_id

    def start(self):
        for sig in self.stop_signals:
            signal.signal(sig, self.__signal_handler)

        try:
            self.__sock.connect((self.__data_server_ip, self.__data_server_port))
        except:
            logger.error(u"不能连接到数据服务器%s:%s" % (self.__data_server_ip, self.__data_server_port))
            return

        self.__ping.start()

    def __signal_handler(self, signum, frame):
        if signum in self.stop_signals:
            self.__sock.close()
            self.__ping.stop()

    def __send_data(self, data):
        msg = {
            "task_id": self.task_id,
            "task": self.task_name,
            "manifest": "ping",
            "time": time.time(),
            "source": self.__local_ip,
            "data": []
        }

        timestamp = time.time()
        redis_data = {}
        info = []
        for ip, d in data.items():
            packet_loss = d[0]
            average_rtt = d[-1]

            temp = [str(packet_loss)]
            temp.extend(map(lambda x: "%.3f" % x, d[1:]))
            redis_data[ip] = ",".join(temp)

            info.append((ip, packet_loss, average_rtt))

        try:
            send_to_tsdb(data, timestamp, self.debug)
        except Exception as e:
            logger.error(u"发送数据到tsdb失败\n %s" % traceback.format_exc(e))

        with self.__redis.pipeline() as pipe:
            pipe.delete(self.__redis_key)
            pipe.hmset(self.__redis_key, redis_data)
            pipe.set(self.__alive_key, timestamp)

            pipe.execute()

        if self.debug:
            # msg_list = ["%s %s%% %.2f\n" % (ip, packet_loss * 100, average_rtt) for ip, packet_loss, average_rtt in info]
            # self.__sock.send("\n".join(msg_list))
            logger.info("ping ok")
            return

        try:
            msg["data"] = info
            msg_str = json.dumps(msg)
            self.__sock.send("%s:%s," % (len(msg_str), msg_str))
            logger.info(u"发送数据完毕")
        except Exception as e:
            logger.error(u"与数据服务器%s:%s的连接中断" % (self.__data_server_ip, self.__data_server_port))
            logger.error(traceback.format_exc(e))

            try:
                self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.__sock.connect((self.__data_server_ip, self.__data_server_port))
            except:
                pass

        logger.info("ping ok")


if __name__ == "__main__":
    args = parse_args()
    master = Master(args.config, args.debug)
    master.start()